﻿#include "appcommon.h"

#include <QApplication>
#include <QCoreApplication>
#include <QCryptographicHash>
#include <QDebug>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QFileInfoList>
#include <QMessageBox>
#include <QProcess>
#include <QSettings>
#include <QSystemTrayIcon>
#include <QTemporaryDir>
#include <QUrl>
#include <QUuid>
#include <algorithm>

#include "config/appconfig.h"
#include "log/app_log.h"
#include "shell/serviceutils.h"
#include "util/system_util.h"

const QString AppCommon::PMS_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
const QString AppCommon::PMS_DATE_FORMAT = "yyyy-MM-dd";

bool AppCommon::isRunningAsRoot() {
#ifdef Q_OS_WIN
    return false;
#else
    uid_t realUserId = getuid();       // 获取实际用户 ID
    uid_t effectiveUserId = geteuid(); // 获取有效用户 ID

    return (realUserId == 0 && effectiveUserId == 0);
#endif
    return false;
}

QString AppCommon::getLoginUser() {
    QProcess process;
    process.start("logname");
    process.waitForFinished();
    QString output = process.readAllStandardOutput();

    return output.trimmed();
}

int AppCommon::getUserIdByGetent(const QString &username) {
    QProcess process;
    process.start("getent", {"passwd", username});
    process.waitForFinished();

    if (process.exitCode() != 0) {
        qWarning() << "User" << username << "not found (getent failed)";
        return -1;
    }

    // getent 输出格式：username:x:UID:GID:description:home:shell
    // 例如：work:x:1000:1000:Work User:/home/work:/bin/bash
    QString output = process.readAllStandardOutput().trimmed();
    QStringList fields = output.split(':');

    if (fields.size() < 3) {
        qWarning() << "Invalid getent output:" << output;
        return -1;
    }

    bool ok;
    int uid = fields[2].toInt(&ok);

    if (!ok) {
        qWarning() << "Failed to parse UID from getent output";
        return -1;
    }

    return uid;
}

void AppCommon::closeDialog(QWidget *dialog) {
    QWidget *parent = getRootParent(dialog);
    if (parent) {
        parent->close();
    } else {
        dialog->close();
    }
}

QWidget *AppCommon::getRootParent(QWidget *widget) {
    QWidget *parent = widget;

    while (parent
           && !(parent->inherits("FramelessDialog") || parent->inherits("FramelessWindow"))) {
        parent = parent->parentWidget();
    }

    return parent;
}

void AppCommon::showWindowToCenter(QWidget *window, QSize size, bool isExec) {
    if (size.width() > 0 && size.height() > 0) {
        window->setGeometry(0, 0, size.width(), size.height());
    }
    window->move(QApplication::desktop()->screen()->rect().center() - window->rect().center());

    if (isExec) {
        if (QDialog *win = qobject_cast<QDialog *>(window)) {
            win->exec();
            return;
        }
    }
    window->showNormal();
}

QString AppCommon::extractFileName(const QString &filePath) {
    int lastSlashIndex = filePath.lastIndexOf('/'); // 找到最后一个 '/' 的位置
    if (lastSlashIndex != -1) {
        return filePath.mid(lastSlashIndex + 1); // 从 '/' 后面开始截取到最后
    } else {
        return filePath; // 如果没有 '/'，说明整个字符串就是文件名
    }
}

bool AppCommon::isLoopbackAddress(const QString &ip) {
    QString t_ip = ip.trimmed();
    if (ip == "127.0.0.1" || ip == "localhost") {
        return true;
    }
    return false;
}

QString AppCommon::getNetworkFromIP(const QString &ip) {
    QStringList ipParts = ip.split('.');
    if (ipParts.size() >= 3) {
        return ipParts[0] + "." + ipParts[1] + "." + ipParts[2];
    }
    return ip;
}

bool AppCommon::isValidIPv4(const QString &ip) {
    // IPv4地址的正则表达式
    QRegularExpression ipv4Regex(R"(^(\d{1,3}\.){3}\d{1,3}(:\d+)?$)");

    // 检查是否符合IPv4的格式
    if (!ipv4Regex.match(ip).hasMatch()) {
        return false;
    }

    // 分离IP地址和端口号（如果存在）
    QString ipAddress;
    QString port;
    int colonIndex = ip.indexOf(':');
    if (colonIndex != -1) {
        ipAddress = ip.left(colonIndex);
        port = ip.mid(colonIndex + 1);
    } else {
        ipAddress = ip;
    }

    // 分割IP地址为四部分
    QStringList parts = ipAddress.split('.');

    // 检查每一部分是否在0到255之间
    for (const QString &part : parts) {
        int num = part.toInt();
        if (num < 0 || num > 255) {
            return false;
        }
    }

    // 如果有端口号，检查端口号是否合法（可选）
    if (!port.isEmpty()) {
        int portNum = port.toInt();
        if (portNum < 0 || portNum > 65535) {
            return false;
        }
    }

    return true;
}

bool AppCommon::isValidNetwork(const QString &network) {
    // IPv4地址的正则表达式
    QRegularExpression ipv4Regex(R"(^(\d{1,3}\.){2}\d{1,3}$)");

    // 检查是否符合IPv4的格式
    return ipv4Regex.match(network).hasMatch();
}

QString AppCommon::addDefaultPort(const QString &ipPort, const QString &defaultPort) {
    // 检查是否包含端口号或只有冒号
    QRegularExpression portRegex(R"(^(\d{1,3}\.){3}\d{1,3}(:\d+)?$)");

    if (portRegex.match(ipPort).hasMatch()) {
        // 如果已经包含端口号或只有冒号，直接返回
        if (ipPort.contains(':')) {
            if (!ipPort.endsWith(':')) {
                return ipPort;
            } else {
                // 只有冒号，添加默认端口号8888
                return ipPort + defaultPort;
            }
        } else {
            // 不符合格式，返回原字符串
            return ipPort + ":" + defaultPort;
        }
    }
    return "";
}

bool AppCommon::checkAutoStart() {
#ifdef Q_OS_WIN
    // Windows下检查开机自启
    QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",
                       QSettings::NativeFormat);
    QString appName = getAppServiceName();
    return settings.contains(appName);
#elif defined(Q_OS_LINUX)
    // Ubuntu下检查开机自启
    return ServiceUtils::isEnabled(getAppServiceName());
#else
    // 未知操作系统
    return false;
#endif
}

bool AppCommon::setAppAutoStart(bool enable) {
    bool nowStartType = checkAutoStart();
    if (nowStartType == enable) {
        return true;
    }
#ifdef Q_OS_WIN
    // Windows下设置开机自启
    QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",
                       QSettings::NativeFormat);
    QString appName = getAppServiceName();
    if (enable) {
        QString appPath = QCoreApplication::applicationFilePath();
        settings.setValue(appName, QDir::toNativeSeparators(appPath));
    } else {
        settings.remove(appName);
    }
    return true;
#elif defined(Q_OS_LINUX)
    // Ubuntu下设置开机自启
    ShellUtils::ShellResult result;
    QString operateType;
    if (enable) {
        operateType = QObject::tr("设置开机自动启动");
        result = ServiceUtils::operate(getAppServiceName(), "enable");
    } else {
        operateType = QObject::tr("取消开机自动启动");
        result = ServiceUtils::operate(getAppServiceName(), "disable");
    }

    return true;
#else
    // 未知操作系统
    qWarning() << "UnSupported platform";
    return false;
#endif
}

bool AppCommon::isSystemTrayAvailable() {
    return QSystemTrayIcon::isSystemTrayAvailable();
}

void AppCommon::exitApp() {
    cppc::log::closeAppLog();
#ifdef Q_OS_LINUX
    ServiceUtils::operate(AppCommon::getAppServiceName(), "stop"); // 关闭服务
#endif
    QCoreApplication::exit();
}

bool AppCommon::isLocalHostUrl(QString url) {
    QUrl serverUrl(url);
    if (!serverUrl.isValid()) {
        return false;
    }
    // 是否访问本机地址
    QString urlIP = serverUrl.host().toUpper();
    bool isLocalIP = urlIP.contains("127.0.0.1") || urlIP.contains("localhost");
    if (!isLocalIP) {
        std::vector<cppc::NetInterfaceInfo> netList = cppc::SystemUtil::getNetInterfaceList();
        for (cppc::NetInterfaceInfo net : netList) {
            if (QString::fromStdString(net.ip).toUpper() == urlIP) {
                isLocalIP = true;
                break;
            }
        }
    }
    return isLocalIP;
}

QString AppCommon::extractNumericPart(const QString &str) {
    QString t_str = str.trimmed();
    QString result;
    for (int i = 0; i < t_str.size(); ++i) {
        if (t_str[i].isDigit()) {
            result += t_str[i];
        } else if (!result.isEmpty()) {
            break; // 遇到非数字字符时停止
        }
    }
    return result;
}
QDateTime AppCommon::parseDateTime(QString time) {
    return QDateTime::fromString(time, PMS_DATE_TIME_FORMAT);
}

QString AppCommon::formatDateTime(QDateTime time) {
    if (time.isNull()) {
        return "";
    }
    return time.toString(PMS_DATE_TIME_FORMAT);
}

QString AppCommon::formatDate(QDateTime time) {
    if (time.isNull()) {
        return "";
    }
    return time.toString(PMS_DATE_FORMAT);
}

QString AppCommon::formatSecondTime(int seconds) {
    QString result;

    int years = seconds / SECONDS_PER_YEAR;
    seconds %= SECONDS_PER_YEAR;
    if (years > 0) {
        result += QString::number(years) + QObject::tr("年");
    }

    int months = seconds / SECONDS_PER_MONTH;
    seconds %= SECONDS_PER_MONTH;
    if (months > 0) {
        result += QString::number(months) + QObject::tr("月");
    }

    int days = seconds / SECONDS_PER_DAY;
    seconds %= SECONDS_PER_DAY;
    if (days > 0) {
        result += QString::number(days) + QObject::tr("天");
    }

    int hours = seconds / SECONDS_PER_HOUR;
    seconds %= SECONDS_PER_HOUR;
    if (hours > 0) {
        result += QString::number(hours) + QObject::tr("时");
    }

    int minutes = seconds / SECONDS_PER_MINUTE;
    seconds %= SECONDS_PER_MINUTE;
    if (minutes > 0) {
        result += QString::number(minutes) + QObject::tr("分");
    }

    if (seconds > 0 || result.isEmpty()) {
        result += QString::number(seconds) + QObject::tr("秒");
    }

    return result;
}

QDateTime AppCommon::getPCBootTime() {
    // 获取当前的系统时间
    QDateTime currentTime = QDateTime::currentDateTime();

    // 获取当前的 tick count
    auto now = std::chrono::steady_clock::now();
    auto tickCount = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();

    // 计算开机时间，减去 tick count 得到开机的 QDateTime
    QDateTime bootTime = currentTime.addMSecs(-tickCount);

    return bootTime;
}

bool AppCommon::writeStringToFile(const QString &filePath, const QString &content) {
    QFile file(filePath);                                     // 创建一个QFile对象，指定文件路径
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { // 打开文件用于写入
        qDebug() << "Failed to open file:" << filePath;
        return false;
    }

    // 提取目标文件的目录路径
    QString dirPath = QFileInfo(filePath).absolutePath();

    // 检查目标目录是否存在，如果不存在则创建
    QDir dir(dirPath);
    if (!dir.exists()) {
        if (!dir.mkpath(dirPath)) {
            return false;
        }
    }

    QTextStream out(&file); // 创建一个QTextStream对象，关联到文件
    out << content;         // 将字符串写入文件

    file.close(); // 关闭文件
    return true;
}

bool AppCommon::copyFile(const QString &sourceFilePath, const QString &destinationFilePath) {
    // 检查源文件是否存在
    QFile sourceFile(sourceFilePath);
    if (!sourceFile.exists()) {
        return false;
    }

    // 提取目标文件的目录路径
    QFileInfo targetFile(destinationFilePath);
    QString destinationDir = targetFile.absolutePath();

    // 检查目标目录是否存在，如果不存在则创建
    QDir dir(destinationDir);
    if (!dir.exists()) {
        if (!dir.mkpath(destinationDir)) {
            return false;
        }
    }

    // 执行文件复制操作
    if (targetFile.exists()) {
        if (!QFile(destinationFilePath).remove()) {
            return false;
        }
    }
    return sourceFile.copy(destinationFilePath);
}

bool AppCommon::fileExist(const QString &filePath) {
    QFile file(filePath);
    return file.exists();
}
QString AppCommon::getExistingDirectoryAsUser(const QString &caption, const QString &dir, const QStringList &tagDirList, QWidget *parent) {
    QFileDialog dialog(parent);
    dialog.setFileMode(QFileDialog::Directory);
    dialog.setOption(QFileDialog::DontUseNativeDialog, true);
    dialog.setOption(QFileDialog::ShowDirsOnly, false);

    QString user = getLoginUser();
    // 添加常用目录到侧边栏
    QList<QUrl> urls;
    if (!user.isEmpty() && user != "root") {
        urls << QUrl::fromLocalFile("/home/" + user);
    }
    for (QString tagDir : tagDirList) {
        urls << QUrl::fromLocalFile(tagDir);
    }

    dialog.setSidebarUrls(urls);

    if (dialog.exec()) {
        QString selectedDir = dialog.selectedFiles().first();
        return selectedDir;
    }
    return "";
}

bool AppCommon::createDir(const QString &dirPath) {
    QDir tempDir(dirPath);
    bool flag = tempDir.mkpath(dirPath);
#ifdef Q_OS_LINUX
    if (flag) {
        mode_t permissions = 0777; // 设置目录权限为 777
        chmod(dirPath.toStdString().c_str(), permissions);
    }
#endif
    return flag;
}

bool AppCommon::chmodDirTo777(const QString &dirPath) {
#ifdef Q_OS_LINUX
    mode_t permissions = 0777; // 设置目录权限为 777
    chmod(dirPath.toStdString().c_str(), permissions);
    return true;
#endif
    return false;
}

bool AppCommon::chmodFileTo666(const QString &filePath) {
#ifdef Q_OS_LINUX
    mode_t permissions = 0666; // 设置文件权限为 666
    chmod(filePath.toStdString().c_str(), permissions);
    return true;
#endif
    return false;
}

bool AppCommon::removeDir(const QString &dirPath) {
    QDir dir(dirPath);
    if (!dir.exists()) {
        return true;
    }

    QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
    for (const QFileInfo &fileInfo : fileList) {
        QString filePath = fileInfo.absoluteFilePath();

        if (fileInfo.isDir()) {
            if (!removeDir(filePath)) {
                return false;
            }
        } else {
            if (!QFile::remove(filePath)) {
                return false;
            }
        }
    }

    return dir.rmdir(dirPath);
}

/// 移除子文件夹，按修改时间删除旧的
/// \param dirPath 父文件夹路径
/// \param saveCount 保留几个新的
int AppCommon::removeOldSubDir(const QString &dirPath, int saveCount) {
    int removeCount = 0;
    QDir dir(dirPath);
    if (!dir.exists()) {
        return removeCount;
    }

    QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    std::sort(dirList.begin(), dirList.end(), [](const QFileInfo &a, const QFileInfo &b) { return a.lastModified() < b.lastModified(); });

    // 遍历排序后的子文件夹列表
    for (int i = 0; i < dirList.size() - saveCount; ++i) {
        const QFileInfo &dirInfo = dirList.at(i);
        if (removeDir(dirInfo.absoluteFilePath())) {
            removeCount++;
        }
    }

    return removeCount; // 返回删除的文件夹数量
}

QStringList AppCommon::getSubDirNameList(const QString &dirPath) {
    QStringList nameList;

    QDir dir(dirPath);
    if (!dir.exists()) {
        return nameList;
    }

    QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    std::sort(dirList.begin(), dirList.end(), [](const QFileInfo &a, const QFileInfo &b) { return a.lastModified() < b.lastModified(); });

    // 遍历排序后的子文件夹列表
    for (int i = 0; i < dirList.size(); ++i) {
        const QFileInfo &dirInfo = dirList.at(i);
        nameList.append(dirInfo.fileName());
    }
    return nameList;
}

QString AppCommon::getParentDirPath(const QString &filePath) {
    QFileInfo fileInfo(filePath);
    if (fileInfo.exists()) {
        return fileInfo.absolutePath();
    }
    return "";
}

QStringList AppCommon::findAllSubDirPath(QString dirPath) {
    QStringList dirList;
    QDir dir(dirPath);
    if (!dir.exists()) {
        return dirList;
    }

    QFileInfoList fileList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
    for (const QFileInfo &fileInfo : fileList) {
        QString filePath = fileInfo.absoluteFilePath();

        if (fileInfo.isDir()) {
            dirList.append(filePath);
        }
    }

    return dirList;
}

QString AppCommon::createTempDirPath() {
    QString tempDirPath = QDir::tempPath() + QDir::separator() + QUuid::createUuid().toString();
    QDir tempDir(tempDirPath);

    if (!tempDir.mkpath(tempDirPath)) {
        return QString();
    }

    return tempDirPath;
}

QString AppCommon::findFirstFileRecursively(const QString &searchDir, const QString &fileName) {
    QDir dir(searchDir);
    if (!dir.exists()) {
        return QString();
    }

    // 遍历当前目录中的所有文件和子目录
    QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
    for (const QFileInfo &fileInfo : fileInfoList) {
        if (fileInfo.isFile() && fileInfo.fileName() == fileName) {
            // 如果是文件且文件名匹配，则返回文件的路径
            return fileInfo.absoluteFilePath();
        }
    }
    for (const QFileInfo &fileInfo : fileInfoList) {
        if (fileInfo.isDir()) {
            // 如果是目录，则递归调用 findFirstFileRecursively 函数
            QString filePath = findFirstFileRecursively(fileInfo.absoluteFilePath(), fileName);
            if (!filePath.isEmpty()) {
                // 如果找到了文件，则立即返回文件路径
                return filePath;
            }
        }
    }

    // 如果没有找到文件，则返回空字符串
    return QString();
}

QString AppCommon::findFirstFileExtRecursively(const QString &searchDir, const QString &fileExt) {
    QDir dir(searchDir);
    if (!dir.exists()) {
        return QString();
    }

    QString fileExtLower = fileExt.trimmed().toLower();

    // 遍历当前目录中的所有文件和子目录
    QFileInfoList fileInfoList = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
    for (const QFileInfo &fileInfo : fileInfoList) {
        if (fileInfo.isFile() && fileInfo.suffix().toLower() == fileExtLower) {
            // 如果是文件且文件名匹配，则返回文件的路径
            return searchDir;
        }
    }
    for (const QFileInfo &fileInfo : fileInfoList) {
        if (fileInfo.isDir()) {
            // 如果是目录，则递归调用 findFirstFileRecursively 函数
            QString filePath = findFirstFileExtRecursively(fileInfo.absoluteFilePath(), fileExtLower);
            if (!filePath.isEmpty()) {
                // 如果找到了文件，则立即返回文件路径
                return filePath;
            }
        }
    }

    // 如果没有找到文件，则返回空字符串
    return QString();
}

ShellUtils::ShellResult AppCommon::extractArchive(const QString &archivePath) {
    QString tempDirPath = createTempDirPath();

    QStringList args;
    args << "-xvf" << archivePath << "-C" << tempDirPath;

    QString command;
    QString lowwerPath = archivePath.toLower();
    if (lowwerPath.endsWith(".zip", Qt::CaseInsensitive)) {
        command = "unzip";
    } else if (lowwerPath.endsWith(".tar.gz", Qt::CaseInsensitive)
               || lowwerPath.endsWith(".tgz", Qt::CaseInsensitive)) {
        command = "tar";
        args.prepend("-z");
    } else if (lowwerPath.endsWith(".gz", Qt::CaseInsensitive)) {
        command = "gzip";
        args.prepend("-d");
    } else if (lowwerPath.endsWith(".tar", Qt::CaseInsensitive)) {
        command = "tar";
    } else {
        return ShellUtils::ShellResult::errorResult(QObject::tr("不支持的压缩格式"), 0);
    }

    ShellUtils::ShellResult result = ShellUtils::runCommand(command, args);
    if (result.success) {
        result.result = tempDirPath;
    }
    return result;
}

bool AppCommon::isArchiveFile(const QString &filePath) {
    QString lowwerPath = filePath.toLower();
    if (lowwerPath.endsWith(".zip", Qt::CaseInsensitive)) {
        return true;
    } else if (lowwerPath.endsWith(".tar.gz", Qt::CaseInsensitive)
               || lowwerPath.endsWith(".tgz", Qt::CaseInsensitive)) {
        return true;
    } else if (lowwerPath.endsWith(".gz", Qt::CaseInsensitive)) {
        return true;
    } else if (lowwerPath.endsWith(".tar", Qt::CaseInsensitive)) {
        return true;
    } else {
        return false;
    }
}

bool AppCommon::checkForSpecificFiles(const QString &folderPath, const QString &fileExtension) {
    QDir dir(folderPath);
    if (!dir.exists()) {
        return false; // 文件夹不存在
    }

    // 构造文件名过滤器，例如 "*.txt" 表示检查扩展名为 .txt 的文件
    QStringList filters;
    filters << ("*" + fileExtension);

    // 获取文件夹中的文件列表
    QStringList files = dir.entryList(filters, QDir::Files);

    // 如果文件列表不为空，则表示存在指定类型的文件
    return !files.isEmpty();
}

QStringList AppCommon::getSubFolders(const QString &folderPath) {
    QStringList subFolders;
    QDir dir(folderPath);
    dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); // 排除 "." 和 ".."
    subFolders = dir.entryList();

    return subFolders;
}

QString AppCommon::getAppCacheRootPath() {
    return getAppRootPath();
}

QString AppCommon::getAppRootPath() {
    QString appDir = QCoreApplication::applicationDirPath();
    return appDir;
}

QString AppCommon::getAppServiceName() {
    return "ep-vpn-client-tool";
}

bool AppCommon::isNullOrEmpty(const QString &str) {
    if (str.isNull()) {
        return true;
    }
    return str.trimmed().isEmpty();
}

QString AppCommon::getFileNameFromFilePath(const QString &filePath) {
    QFileInfo fileInfo(filePath);
    return fileInfo.fileName();
}

void _setTreeCellAlignCenter(QTreeWidget *treeWidget, QTreeWidgetItem *item, QList<int> columns) {
    // 遍历项的所有列
    for (int col = 0; col < treeWidget->columnCount(); ++col) {
        // 检查当前列是否应该居中对齐
        if (columns.isEmpty() || columns.contains(col)) {
            // 设置文本对齐方式为居中
            item->setTextAlignment(col, Qt::AlignCenter);
        }
    }
    // 递归设置子项的对齐方式
    for (int i = 0; i < item->childCount(); ++i) {
        _setTreeCellAlignCenter(treeWidget, item->child(i), columns);
    }
}

void _setTreeCellTooltip(QTreeWidget *treeWidget, QTreeWidgetItem *item) {
    // 遍历项的所有列
    for (int col = 0; col < treeWidget->columnCount(); ++col) {
        if (item->toolTip(col).trimmed().isEmpty()) {
            item->setToolTip(col, item->text(col));
        }
    }
    // 递归设置子项的对齐方式
    for (int i = 0; i < item->childCount(); ++i) {
        _setTreeCellTooltip(treeWidget, item->child(i));
    }
}
bool AppCommon::isValidUrl(const QString &urlString, bool emptyValid) {
    QString urlText = urlString.trimmed();
    if (urlText.isEmpty()) {
        return emptyValid;
    }
    QStringList ipInfo = urlText.split("|");
    if (ipInfo.size() >= 1) {
        QString ip = ipInfo.at(0).trimmed();
        QUrl url(ip);
        return url.isValid();
    }
    return emptyValid;
}

bool AppCommon::isValidIPPort(const QString &ipPortString)
{
    // 使用正则表达式验证格式
      QRegularExpression regex("^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})$");
      QRegularExpressionMatch match = regex.match(ipPortString);

      if (!match.hasMatch()) {
          return false;
      }

      QString ip = match.captured(1);
      QString portStr = match.captured(2);
      bool ok;
      int port = portStr.toInt(&ok);

      // 检查端口是否在有效范围内
      if (!ok || port < 1 || port > 65535) {
          return false;
      }

      // 检查IP各部分是否有效
      QStringList parts = ip.split('.');
      if (parts.size() != 4) {
          return false;
      }

      for (const QString &part : parts) {
          int num = part.toInt(&ok);
          if (!ok || num < 0 || num > 255) {
              return false;
          }
      }

      return true;
}

QString AppCommon::calculateFileMD5(const QString &filePath) {
    // 打开文件
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly)) {
        return QString();
    }

    // 计算MD5值
    QCryptographicHash hash(QCryptographicHash::Md5);
    while (!file.atEnd()) { hash.addData(file.read(8192)); }
    file.close();

    // 返回MD5值的十六进制表示
    return hash.result().toHex();
}

void AppCommon::ensureAInFrontOfB(QList<QString> &a, QList<QString> &b) {
    for (int i = a.size() - 1; i >= 0; i--) {
        auto item = a[i];
        int index = b.indexOf(item);
        if (index == -1) {
            b.insert(0, item);
        } else {
            if (index != 0) {
                b.move(index, 0);
            }
        }
    }
}
